/*
 * Project: BoboDesignPattern
 * 
 * File Created at 2019年8月20日
 * 
 * Copyright 2012 Greenline.com Corporation Limited.
 * All rights reserved.
 *
 * This software is the confidential and proprietary information of
 * Greenline Company. ("Confidential Information").  You shall not
 * disclose such Confidential Information and shall use it only in
 * accordance with the terms of the license agreement you entered into
 * with Greenline.com.
 */
package test.net;

import java.net.InetSocketAddress;
import java.nio.ByteBuffer;
import java.nio.channels.SelectionKey;
import java.nio.channels.Selector;
import java.nio.channels.ServerSocketChannel;
import java.nio.channels.SocketChannel;
import java.util.Iterator;
import java.util.Set;

/**
 * @Type ServerSocketNioStandard
 * @Desc
 * @author 52489
 * @date 2019年8月20日
 * @Version V1.0
 */
public class ServerSocketNioStandard implements Runnable {

    public int id = 100001;
    public int bufferSize = 2048;

    @Override
    public void run() {
        // TODO Auto-generated method stub
        init();
    }

    /**
     * 
     */
    public void init() {
        try {

            ServerSocketChannel socketChannel = ServerSocketChannel.open();
            Selector selector = Selector.open();
            InetSocketAddress inetSocketAddress = new InetSocketAddress("127.0.0.1", 8888);
            socketChannel.socket().bind(inetSocketAddress);
            // 设置通道非堵塞，绑定选择器
            socketChannel.configureBlocking(false);
            socketChannel.register(selector, SelectionKey.OP_ACCEPT).attach(id++);
            System.out.println("Server started .. ");
            listener(selector);
        } catch (Exception e) {
            // TODO: handle exception
        }
    }

    /**
     * @param selector
     */
    private void listener(Selector selector) {
        try {
            while (true) {
                Thread.sleep(1000);
                selector.select(); // 堵塞直到有就绪事件位置
                Set<SelectionKey> readySelectionKey = selector.selectedKeys();
                Iterator<SelectionKey> it = readySelectionKey.iterator();
                while (it.hasNext()) {
                    SelectionKey selectionKey = it.next();
                    // 判断是哪个事件
                    if (selectionKey.isAcceptable()) {
                        System.out.println(selectionKey.attachment() + " - 接受请求事件");
                        // 获取通道，接受连接，
                        // 设置非堵塞模式（必须）同时需要注册读写数据的事件，这样有消息触发才会捕捉
                        ServerSocketChannel serverSocketChannel = (ServerSocketChannel) selectionKey.channel();
                        serverSocketChannel.accept().configureBlocking(false)
                                .register(selector, SelectionKey.OP_READ | SelectionKey.OP_WRITE).attach(id++);
                        System.out.println(selectionKey.attachment() + " - 已连接");
                        // 下面这种写法是有问题的 不应该在serverSocketChannel上面注册
                        /*
                         * serverSocketChannel.configureBlocking(false);
                         * serverSocketChannel.register(in_selector,
                         * SelectionKey.OP_READ);
                         * serverSocketChannel.register(in_selector,
                         * SelectionKey.OP_WRITE);
                         */
                    }

                    if (selectionKey.isReadable()) { // 读数据
                        System.out.println(selectionKey.attachment() + " - 读数据事件");
                        SocketChannel clientChannel = (SocketChannel) selectionKey.channel();
                        ByteBuffer receiveBuf = ByteBuffer.allocate(bufferSize);
                        clientChannel.read(receiveBuf);
                        System.out.println(selectionKey.attachment() + " - 读取数据：" + getString(receiveBuf));
                    }

                    if (selectionKey.isWritable()) {// 写数据
                        System.out.println(selectionKey.attachment() + " - 写数据事件");
                        SocketChannel clientChannel = (SocketChannel) selectionKey.channel();
                        ByteBuffer sendBuf = ByteBuffer.allocate(bufferSize);
                        String sendText = "hello\n";
                        sendBuf.put(sendText.getBytes());
                        sendBuf.flip(); // 写完数据后调用此方法
                        clientChannel.write(sendBuf);
                    }
                    if (selectionKey.isConnectable()) {
                        System.out.println(selectionKey.attachment() + " - 连接事件");
                    }
                    // 必须removed 否则会继续存在，下一次循环还会进来,
                    // 注意removed 的位置，针对一个.next() remove一次
                    it.remove();
                }
            }

        } catch (Exception e) {
            // TODO: handle exception
        }
    }

    /**
     * ByteBuffer 转换 String
     * 
     * @param buffer
     * @return
     */
    public static String getString(ByteBuffer buffer) {
        String string = "";
        try {
            for (int i = 0; i < buffer.position(); i++) {
                string += (char) buffer.get(i);
            }
            return string;
        } catch (Exception ex) {
            ex.printStackTrace();
            return "";
        }
    }

    public static void main(String[] args) {
        Thread server = new Thread(new ServerSocketNioStandard());
        server.start();
    }
}
